#include "conv.h"

using namespace std;

//ref https://www.cnblogs.com/hejunlin1992/p/8686838.html
/**
 * @brief 矩阵乘法实现卷积
 * @param inframe，输入图像,应该为单通道
 * @param kernel，卷积核
 * @param padding，padding数组指针，如果不指定默认做samepadding
 * @param padvalue，填充值，默认为0
 * @param stride ，卷积步长，默认为1,当前仅支持1
 * @return output,输出图像，CV_32FC1
 */ 
cv::Mat conv2d(const cv::Mat &inframe,cv::Mat kernel,int* padding,int pad_value,int stride)
{
    if (stride != 1)
    {
        cout<<"unsupport stride ,current version only support stride =1"<<endl;
    }
    int w = inframe.cols;
    int h = inframe.rows;
    int top,bottom,left,right;
    int kernel_w = kernel.cols;
    int kernel_h = kernel.rows;
    //计算padding
    if (padding == nullptr){
        //<0默认same padding
        left = (kernel_w-1)/2;
        right = kernel_w-1 - left;
        top = (kernel_h-1)/2;
        bottom = kernel_h-1 - top;
    }
    else
    {
        left = *padding;
        right = *(padding + 1);
        top = *(padding +2);
        bottom = *(padding +3);
    }
    //最后输出尺寸为[h_new,w_new]
    int h_new = h-kernel_h + top + bottom +1;//row
    int w_new = w - kernel_w + left + right + 1 ;//cols
    cv::Mat pad_img;
    //做padding,实际上最好不要这样实现,但是我不会...
    cv::copyMakeBorder(inframe,pad_img,top,bottom,left,right,cv::BORDER_CONSTANT,cv::Scalar(pad_value));
    // std::cout<<pad_img.cols<<","<<pad_img.rows<<",h_new:"<<h_new<<",w_new"<<w_new<<std::endl;
    // std::cout<<kernel_h<<","<<kernel_w<<std::endl;
    //shape = (h+top+bottom,w+left+right)
    cv::Mat im2col((h_new*w_new),(kernel_h*kernel_w),CV_32F,cv::Scalar(pad_value));
    cv::Mat flatten_kernel = kernel.reshape(1,kernel_h*kernel_w);//(k_h*h_w,1)
    //对im2col进行赋值
    for(unsigned int i=0;i<h_new;i++){
        //row
        for(unsigned int j=0;j<w_new;j++)
        { //col
            float* row_ptr = im2col.ptr<float>(i*w_new + j);
            //对应第i*h_new + j行的第一个元素指针
            //从i*(h+top+bottom) + j开始
            //第一个元素对应原始图像为(i,j)
            //填充第i*h_new + j行
            for (unsigned int k_i=0;k_i<kernel_h;k_i++){
                for(unsigned int k_j=0;k_j<kernel_w;k_j++){
                    *(row_ptr+k_i*kernel_h+k_j) = pad_img.at<unsigned char>(i+k_i,j+k_j);
                }
            } 
        }
    }
    //对im2col赋值
    // cout<<im2col.rows<<","<<im2col.cols<<","<<flatten_kernel.rows<<","<<flatten_kernel.cols<<endl;
    cv::Mat tmp = im2col*flatten_kernel;//(h_new*w_new,1)
    // cout<<"tmp shape"<<tmp.cols<<","<<tmp.rows<<h_new<<w_new<<endl;
    cv::Mat output = cv::Mat(h_new,w_new,CV_32FC1,tmp.data);
    // cv::Mat output = cv::Mat(h_new,w_new,CV_32FC1,tmp.ptr<float>());
    //返回clone，否则还是用之前tmp的内存
    return output.clone();
    
}